require({
  baseUrl: ""
}, ["treehugger/tree",
"treehugger/traverse",
"treehugger/js/parse",
"json2",
"jquery"
], function(tree, traverse, parsejs) {

  function output(text) {
    $('#output').val(text);
  }

  function update() {
    var source, ast;

    // Clear console
    console.clear();

    // Test input
    source = $("#input").val();
    
    // Parse the AST tree using uglify
    ast = parsejs.parse(source);
    traverse.addParentPointers(ast);

    // Process the batch block
    processBatch(ast[0]);
  }

  function processBatch(ast) {
    var identifier, service, serviceName,
        out, outString, input;

    identifier = ast[0];
    service = ast[1];
    block = ast[2];
    /*  Identifier for the root of the connection.
    *   This has to be a let declaration because it is restricted to the
        batch statement.
        Note that this identifier is not used in the script, the
        connection/service alone should be enough However, declaring the
        root is syntactically more correct.
        You don't say "get the products table from the connection" but
        "get the products table from the database" and "get the database
        from the connection".
        Furthermore, Cook did the same with Jaba (didn't use let though).
    */
      identifier.traverseTopDown(
      'LetDecl(name),', function(b) {
        identifier = b.name.value;
      }
    );
    if( !identifier ) 
      throw "illegal batch statement";

    if( service.cons == "ERROR" ) {
      throw "illegal batch statement";
    }
    serviceName = service[0].value;

    $('#ast').val(block[0].toPrettyString())

    // Parse ast for first time, making annotations to generate script
    ast = makeAnnotations(block[0], identifier);

    // Input for the batch script
    input = {};

    out = generateCode(ast);

    // Start of batch closure
    outString  = '(function('+serviceName+'){\n';
    outString += 'var input = '+JSON.stringify(input)+';\n';
    // Body of batch closure
    outString += out.join('\n');
    // End of batch closure
    outString += '})('+serviceName+');';

    $('#output').val(outString);
    $('#ast_new').val(ast.toPrettyString());
  }

  /* stappenplan:
  * 1: Alle illegale operaties binnen batch statement breken compilen af
        not yet implemented:
          let
          function definition
          ternary if

        fundementally illegal:
          batch (batch in batch)
          (do)while (we use forEach)
          for (we use forEach)
  */
  function makeAnnotations(ast, identifier) {
    var valid, forEachCall, forEachPath, breakTraversal;

    x = ast;

    // Find all remote operations
    breakTraversal = false;
    ast.traverseTopDown(
      'Var("'+identifier+'")', function() {
        this.traverseUp(function() {
          if( breakTraversal ) return false;
          if( this.parent.cons == "Batch" ) breakTraversal = true;
          else this.setAnnotation("Remote", true);
        });
      }
    );
    console.log(ast);
    return ast;

    ast.traverseTopDown(
      'LetDecls(_)', function() {
        throw "syntax error, let expression in batch is not yet implemented";
      },
      'TernaryIf(_,_,_)', function() {
        throw "syntax error, ternary expression in batch is not yet implemented";
      },
      'Batch(_)', function() {
        throw "syntax error, batch do..statements cannot be nested";
      },
      'For(_,_,_,_)', function() {
        throw "syntax error, for in batch is illegal";
      },
      'Do(_,_)', function() {
        throw "syntax error, do..while in batch is illegal";
      },
      'While(_,_)', function() {
        throw "syntax error, while in batch is illegal";
      },
    /*
      2: Foreach
    */
      'Call(target,args)', function(b) {
        valid = false;
        if( b.target.cons == 'PropAccess' ) {   
          b.target.traverseTopDown('PropAccess(subject,"forEach")', function(b) {
            // ForEach is only valid when the remote root is in it
            this.traverseTopDown('Var("'+identifier+'")', function() {
              valid = true;
            });
          });

          if( valid ) {
            // Callback method in forEach should not generate a not yet implemented exception
            b.args[0].setAnnotation('forEachCallback', true);

            //traverseForEach(b.target);
            forEachCall = [];
            forEachPath;
            b.target[0].traverseTopDown(
              'PropAccess(next,name)', function(b) {
                forEachCall.push(b.name.value);
              },
              'Var(name)', function(b) {
                forEachPath = b.name.value;
              }
            );
            for( var i = forEachCall.length; i > 0; i--) {
              forEachPath = forEachPath + '.' + forEachCall[i-1];
            }
            this.setAnnotation('forEachPath', forEachPath);
          }
        }

        if( !valid ) {
          throw "syntax error, call in batch is not yet implemented";
        }
        //if( b.x == PropAccess(_,"forEach") ) {
          //console.log("hoi"); 
        //}
        //console.log('PropAccess(Var("'+identifier+'"), prop');
      },
      'Function(_,_,_)', function() {
        if(!this.getAnnotation('forEachCallback')) {
          throw "syntax error, function in batch is not yet implemented";
        }
      }
    );

    return ast;
  }

  function generateCode(ast) {
    var out, scriptbuilder;

    // To create the script, a factory is used. It's basically a stringbuilder.
    scriptbuilder = Scriptbuilder;

    // Array with output javascript lines
    out = [];

    ast.traverseTopDown(function() {
      if( this instanceof tree.ConsNode ) {
        console.log(this.cons);
        switch( this.cons ) {

        }
      }
      else if( this instanceof tree.ListNode ) {

      }
    });

    return out;
  }

  // Traverse the forEach access from .forEach untill the root
  function traverseForEach(propAccess) {
    if( propAccess[0].cons = "PropAccess" ) {
      return traverseForEach(propAccess);
    }
  }

  Scriptbuilder = {
    Op: {
      EQ: "==",
      SEQ: "skip"
    },

    Data: function(data) {

    },

    If: function(stCond, stThen, stElse) {

    },

    In: function(name) {
      
    },

    Loop: function(name, stIterator, stBody) {

    },

    Out: function(name, stValue) {

    },

    Prim: function(operator, args) {

    },

    Prop: function(stSource, prop) {

    },

    Root: function() {

    },

    Var: function(name) {

    }
  }

  require.ready(function() {
    $(document).ready(function() {
      $('#input').keyup(update);  
      update();
    });
  });
});
